package com.huantai.common.util.time;

import java.text.ParseException;
import java.util.Date;

import com.huantai.common.consts.DateTimeFormat;
import org.apache.commons.lang3.time.DurationFormatUtils;
import org.apache.commons.lang3.time.FastDateFormat;

import com.huantai.common.util.ConvertUtil;
import com.huantai.common.util.StringUtil;

/**
 * Date的parse()与format(), 采用Apache Common Lang中线程安全, 性能更佳的FastDateFormat
 * 
 * 注意Common Lang版本，3.5版才使用StringBuilder，3.4及以前使用StringBuffer.
 * 
 * 常用的格式在<code>com.huantai.common.util.time.consts.DateTimeFormat</code>中已定义请直接使用！
 * 
 * @see FastDateFormat#parse(String)
 * @see FastDateFormat#format(java.util.Date)
 * @see FastDateFormat#format(long)
 * @author Ma Chao
 * @version 2018年6月7日 下午8:53:55
 */
public final class DateFormatUtil {
    private DateFormatUtil() {}

    /**
     * 验证字符串是否满足指定的时间格式，同时校验时间的有效性（月份、日期、小时、分钟、秒）
     * <p>
     * <code>DateTimeFormat.Pattern.YYYYMM</code>， <code>DateTimeFormat.Pattern.YYYY_MM</code>，
     * <code>DateTimeFormat.Pattern.YYYYMMDD</code>， <code>DateTimeFormat.Pattern.YYYY_MM_DD</code>，
     * <code>DateTimeFormat.Pattern.YYYYMMDDHHMMSS</code>，
     * <code>DateTimeFormat.Pattern.YYYY_MM_DD_HH_MM_SS</code>
     * </p>
     * 
     * @param pattern 指定的时间格式，支持的格式见方法注释
     * @param dateString 要验证的字符串
     * @return 满足时返回<code>true</code>；否则返回<code>false</code>
     */
    public static boolean validDate(String pattern, String dateString) {
        if (StringUtil.isEmpty(dateString)) {
            return false;
        }
        // 长度不一样，格式不正确
        if (pattern.length() != dateString.length()) {
            return false;
        }
        FastDateFormat format = FastDateFormat.getInstance(pattern);
        try {
            format.parse(dateString);
            return validDetail(pattern, dateString);
        } catch (ParseException e) {
            return false;
        }
    }

    /**
     * String转Date
     * 
     * @param pattern 建议使用<code>com.huantai.common.util.time.consts.DateTimeFormat.Pattern</code>
     *        中定义的pattern
     * @param dateString 要转换的字符串
     * @return 可以转换时返回Date对象，否则返回<code>null</code>
     * @throws ParseException
     */
    public static Date parseDate(String pattern, String dateString) throws ParseException {
        FastDateFormat format = FastDateFormat.getInstance(pattern);
        return format.parse(dateString);
    }

    /**
     * Date转String
     * 
     * @param pattern 建议使用<code>com.huantai.common.util.time.consts.DateTimeFormat.Pattern</code>
     *        中定义的pattern
     * @param date 要转换的日期对象
     * @return pattern格式的日期字符串
     */
    public static String formatDate(String pattern, Date date) {
        return FastDateFormat.getInstance(pattern).format(date);
    }

    /**
     * Date转String
     * 
     * @param pattern 建议使用<code>com.huantai.common.util.time.consts.DateTimeFormat.Pattern</code>
     *        中定义的pattern
     * @param date 要转换的日期时间数字
     * @return pattern格式的日期字符串
     */
    public static String formatDate(String pattern, long date) {
        return FastDateFormat.getInstance(pattern).format(date);
    }

    /**
     * 按HH:mm:ss.SSS格式，格式化时间间隔.
     * 
     * endDate必须大于startDate，间隔可大于1天，
     * 
     * @see DurationFormatUtils
     */
    public static String formatDuration(Date startDate, Date endDate) {
        return DurationFormatUtils.formatDurationHMS(endDate.getTime() - startDate.getTime());
    }

    /**
     * 按HH:mm:ss.SSS格式，格式化时间间隔
     * 
     * 单位为毫秒，必须大于0，可大于1天
     * 
     * @see DurationFormatUtils
     */
    public static String formatDuration(long durationMillis) {
        return DurationFormatUtils.formatDurationHMS(durationMillis);
    }

    /**
     * 按HH:mm:ss格式，格式化时间间隔
     * 
     * endDate必须大于startDate，间隔可大于1天
     * 
     * @see DurationFormatUtils
     */
    public static String formatDurationOnSecond(Date startDate, Date endDate) {
        return DurationFormatUtils.formatDuration(endDate.getTime() - startDate.getTime(),
                "HH:mm:ss");
    }

    /**
     * 按HH:mm:ss格式，格式化时间间隔
     * 
     * 单位为毫秒，必须大于0，可大于1天
     * 
     * @see DurationFormatUtils
     */
    public static String formatDurationOnSecond(long durationMillis) {
        return DurationFormatUtils.formatDuration(durationMillis, "HH:mm:ss");
    }

    /**
     * 打印用户友好的，与当前时间相比的时间差，如刚刚，5分钟前，今天XXX，昨天XXX
     * 
     * copy from AndroidUtilCode
     */
    public static String formatFriendlyTimeSpanByNow(Date date) {
        return formatFriendlyTimeSpanByNow(date.getTime());
    }

    /**
     * 打印用户友好的，与当前时间相比的时间差，如刚刚，5分钟前，今天XXX，昨天XXX
     * 
     * copy from AndroidUtilCode
     */
    public static String formatFriendlyTimeSpanByNow(long timeStampMillis) {
        long now = ClockUtil.currentTimeMillis();
        long span = now - timeStampMillis;
        if (span < 0) {
            // 'c' 日期和时间，被格式化为 "%ta %tb %td %tT %tZ %tY"，例如 "Sun Jul 20 16:17:00 EDT 1969"。
            return String.format("%tc", timeStampMillis);
        }
        if (span < DateUtil.MILLIS_PER_SECOND) {
            return "刚刚";
        } else if (span < DateUtil.MILLIS_PER_MINUTE) {
            return String.format("%d秒前", span / DateUtil.MILLIS_PER_SECOND);
        } else if (span < DateUtil.MILLIS_PER_HOUR) {
            return String.format("%d分钟前", span / DateUtil.MILLIS_PER_MINUTE);
        }
        // 获取当天00:00
        long wee = DateUtil.beginOfDate(new Date(now)).getTime();
        if (timeStampMillis >= wee) {
            // 'R' 24 小时制的时间，被格式化为 "%tH:%tM"
            return String.format("今天%tR", timeStampMillis);
        } else if (timeStampMillis >= wee - DateUtil.MILLIS_PER_DAY) {
            return String.format("昨天%tR", timeStampMillis);
        } else {
            // 'F' ISO 8601 格式的完整日期，被格式化为 "%tY-%tm-%td"。
            return String.format("%tF", timeStampMillis);
        }
    }



    /**
     * 根据格式详细验证，验证了格式：<code>DateTimeFormat.Pattern.YYYYMM</code>，
     * <code>DateTimeFormat.Pattern.YYYY_MM</code>， <code>DateTimeFormat.Pattern.YYYYMMDD</code>，
     * <code>DateTimeFormat.Pattern.YYYY_MM_DD</code>，
     * <code>DateTimeFormat.Pattern.YYYYMMDDHHMMSS</code>，
     * <code>DateTimeFormat.Pattern.YYYY_MM_DD_HH_MM_SS</code>
     * 
     * @param pattern 要验证的格式
     * @param dateString 要验证的数据
     */
    private static boolean validDetail(String pattern, String dateString) {
        boolean result = false;
        // 年月日时分秒
        String year = "";
        String month = "";
        String day = "";
        String hour = "";
        String minute = "";
        String second = "";
        switch (pattern) {
            // yyyyMM
            case DateTimeFormat.Pattern.YYYYMM:
                month = dateString.substring(4);
                result = DateUtil.validMonth(ConvertUtil.toInt(month));
                break;
            // yyyy-MM
            case DateTimeFormat.Pattern.YYYY_MM:
                month = dateString.substring(5);
                result = DateUtil.validMonth(ConvertUtil.toInt(month));
                break;
            // yyyyMMdd
            case DateTimeFormat.Pattern.YYYYMMDD:
                year = dateString.substring(0, 4);
                month = dateString.substring(4, 6);
                day = dateString.substring(6);
                result = DateUtil.validMonth(ConvertUtil.toInt(month)) && DateUtil.validDayOfMonth(
                        ConvertUtil.toInt(year), ConvertUtil.toInt(month), ConvertUtil.toInt(day));
                break;
            // yyyy-MM-dd
            case DateTimeFormat.Pattern.YYYY_MM_DD:
                year = dateString.substring(0, 4);
                month = dateString.substring(5, 7);
                day = dateString.substring(8);
                result = DateUtil.validMonth(ConvertUtil.toInt(month)) && DateUtil.validDayOfMonth(
                        ConvertUtil.toInt(year), ConvertUtil.toInt(month), ConvertUtil.toInt(day));
                break;
            // yyyy-MM-dd HH:mm:ss
            case DateTimeFormat.Pattern.YYYY_MM_DD_HH_MM_SS:
                year = dateString.substring(0, 4);
                month = dateString.substring(5, 7);
                day = dateString.substring(8, 10);
                hour = dateString.substring(11, 13);
                minute = dateString.substring(15, 17);
                second = dateString.substring(18);
                result = DateUtil.validMonth(ConvertUtil.toInt(month))
                        && DateUtil.validDayOfMonth(ConvertUtil.toInt(year),
                                ConvertUtil.toInt(month), ConvertUtil.toInt(day))
                        && DateUtil.validHour(ConvertUtil.toInt(hour))
                        && DateUtil.validMinuteOrSecond(ConvertUtil.toInt(minute))
                        && DateUtil.validMinuteOrSecond(ConvertUtil.toInt(second));
                break;
            // yyyyMMddHHmmss
            case DateTimeFormat.Pattern.YYYYMMDDHHMMSS:
                year = dateString.substring(0, 4);
                month = dateString.substring(4, 6);
                day = dateString.substring(6, 8);
                hour = dateString.substring(8, 10);
                minute = dateString.substring(10, 12);
                second = dateString.substring(12);
                result = DateUtil.validMonth(ConvertUtil.toInt(month))
                        && DateUtil.validDayOfMonth(ConvertUtil.toInt(year),
                                ConvertUtil.toInt(month), ConvertUtil.toInt(day))
                        && DateUtil.validHour(ConvertUtil.toInt(hour))
                        && DateUtil.validMinuteOrSecond(ConvertUtil.toInt(minute))
                        && DateUtil.validMinuteOrSecond(ConvertUtil.toInt(second));
                break;
            default:
                break;
        }
        return result;
    }
}
